---
title: "Uploading and Storing Files"
sidebar_label: "Upload"
sidebar_position: 1
description: "Upload files to Convex storage"
---

import Messages from "!!raw-loader!@site/../demos/file-storage/convex/messages.ts";
import URLUploadTS from "!!raw-loader!@site/../demos/file-storage/src/_simpleApp.tsx";
import URLUploadJS from "!!raw-loader!@site/../private-demos/snippets/src/fileStorageApp.jsx";
import HttpAction from "!!raw-loader!@site/../demos/file-storage-with-http/convex/http.ts";
import HttpUploadTS from "!!raw-loader!@site/../demos/file-storage-with-http/src/_simpleApp.tsx";
import HttpUploadJS from "!!raw-loader!@site/../private-demos/snippets/src/fileStorageWithHttpApp.jsx";

Upload files to Convex by
[generated upload urls](#uploading-files-via-upload-urls), or via an
[custom HTTP Action](#uploading-files-via-an-http-action).

## Uploading files via upload URLs

Arbitrarily large files can be uploaded directly to your backend using a
generated upload URL. This requires the client to make 3 requests:

1. Generate an upload URL using a mutation that calls
   [`storage.generateUploadUrl()`](/api/interfaces/server.StorageWriter#generateuploadurl).
2. Send a POST request with the file contents to the upload URL and receive a
   storage ID.
3. Save the storage ID into your data model via another mutation.

In the first mutation that generates the upload URL you can control who can
upload files to your Convex storage.

**Example**:
[File Storage with Queries and Mutations](https://github.com/get-convex/convex-demos/tree/main/file-storage)

### Calling the upload APIs from a web page

Here's an example of uploading an image via a form submission handler to an
upload URL generated by a mutation:

<TSAndJSSnippet
  title="src/App.jsx"
  sourceTS={URLUploadTS}
  sourceJS={URLUploadJS}
  snippet="fileUpload"
  highlightPatterns={[
    "generateUploadUrl\\(",
    "sendImage\\(",
    "await fetch",
    "method:",
    "headers:",
    "body:",
    "}\\);",
  ]}
/>

### Generating the upload URL

An upload URL can be generated by the
[`storage.generateUploadUrl`](/api/interfaces/server.StorageWriter#generateuploadurl)
function of the [`MutationCtx`](/api/interfaces/server.GenericMutationCtx)
object:

<TSAndJSSnippet
  title="convex/messages.js"
  sourceTS={Messages}
  sourceJS={Messages}
  snippet="generateUploadUrl"
  highlightPatterns={["storage.generateUploadUrl"]}
/>

This mutation can control who is allowed to upload files.

The upload URL expires in 1 hour and so should be fetched shortly before the
upload is made.

### Writing the new storage ID to the database

Since the storage ID is returned to the client it is likely you will want to
persist it in the database via another mutation:

<TSAndJSSnippet
  title="convex/messages.ts"
  sourceTS={Messages}
  sourceJS={Messages}
  prefix={`import { mutation } from "./_generated/server";\n`}
  snippet="saveStorageId"
  highlightPatterns={["storage.generateUploadUrl"]}
/>

### Limits

The file size is not limited, but upload POST request has a 2 minute timeout.

## Uploading files via an HTTP action

The file upload process can be more tightly controlled by leveraging
[HTTP action](/functions/http-actions.mdx)s, performing the whole upload flow
using a single request, but requiring correct CORS headers configuration.

The custom upload HTTP action can control who can upload files to your Convex
storage. But note that the HTTP action request size is
[currently limited](/functions/http-actions.mdx#limits) to 20MB. For larger
files you need to use upload URLs as described
[above](#uploading-files-via-upload-urls).

**Example:**
[File Storage with HTTP Actions](https://github.com/get-convex/convex-demos/tree/main/file-storage-with-http)

### Calling the upload HTTP action from a web page

Here's an example of uploading an image via a form submission handler to the
`sendImage` HTTP action defined next.

The highlighted lines make the actual request to the HTTP action:

<TSAndJSSnippet
  title="src/App.tsx"
  sourceTS={HttpUploadTS}
  sourceJS={HttpUploadJS}
  snippet="httpFileUpload"
  highlightPatterns={["fetch", "method:", "headers:", "body:", "}\\);"]}
/>

### Defining the upload HTTP action

A file sent in the HTTP request body can be stored using the
[`storage.store`](/api/interfaces/server.StorageActionWriter#store) function of
the [`ActionCtx`](/api/interfaces/server.GenericActionCtx) object. This function
returns an `Id<"_storage">` of the stored file.

From the HTTP action you can call a mutation to write the storage ID to a
document in your database.

To confirm success back to your hosted website, you will need to set the right
[CORS headers](/functions/http-actions.mdx#cors):

<TSAndJSSnippet
  title="convex/http.js"
  sourceTS={HttpAction}
  sourceJS={HttpAction}
  snippet="sendImageStore"
  highlightPatterns={["storage.store"]}
/>

You also need to handle the pre-flight `OPTIONS` request:

<TSAndJSSnippet
  title="convex/http.js"
  sourceTS={HttpAction}
  sourceJS={HttpAction}
  snippet="preflight"
  highlightPatterns={["storage.store"]}
/>
